package cn.stimd.spring.transaction.support;

import org.springframework.core.NamedThreadLocal;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;

import java.util.*;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class TransactionSynchronizationManager {
    private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("事务资源");
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<>("事务同步集合");
    private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("事务名称");
    private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal<>("readOnly属性");
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal<>("隔离级别");
    private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("事务活跃标记");


    /**
     * 清空线程绑定的事务
     */
    public static void clear() {
        synchronizations.remove();
        currentTransactionName.remove();
        currentTransactionReadOnly.remove();
        currentTransactionIsolationLevel.remove();
        actualTransactionActive.remove();
    }


    public static boolean isSynchronizationActive() {
        return synchronizations.get() != null;
    }

    public static void setActualTransactionActive(boolean active) {
        actualTransactionActive.set(active ? Boolean.TRUE : null);
    }

    public static boolean isActualTransactionActive() {
        return (actualTransactionActive.get() != null);
    }

    public static void setCurrentTransactionReadOnly(boolean readOnly) {
        currentTransactionReadOnly.set(readOnly ? Boolean.TRUE : null);
    }

    public static Boolean isCurrentTransactionReadOnly() {
        return currentTransactionReadOnly.get() != null;
    }

    public static void setCurrentTransactionName(String name) {
        currentTransactionName.set(name);
    }

    public static String getCurrentTransactionName() {
        return currentTransactionName.get();
    }

    public static void setCurrentTransactionIsolationLevel(Integer isolationLevel) {
        currentTransactionIsolationLevel.set(isolationLevel);
    }

    public static Integer getCurrentTransactionIsolationLevel() {
        return currentTransactionIsolationLevel.get();
    }

    public static void initSynchronization() {
        if (isSynchronizationActive()) {
            throw new IllegalStateException("Cannot activate transaction synchronization - already active");
        }
        synchronizations.set(new LinkedHashSet<>());
    }


    public static void registerSynchronization(TransactionSynchronization synchronization){
        if(!isSynchronizationActive()){
            throw new IllegalStateException("事务同步没有开启");
        }
        synchronizations.get().add(synchronization);
    }


    //返回当前线程绑定的不可更改的Synchronization快照
    public static List<TransactionSynchronization> getSynchronizations(){
        Set<TransactionSynchronization> syncs = synchronizations.get();
        if (syncs == null) {
            throw new RuntimeException("Transaction synchronization is not active");
        }

        if (syncs.isEmpty()) {
            return Collections.emptyList();
        }

        List<TransactionSynchronization> sortedSyncs = new ArrayList<>(syncs);
        AnnotationAwareOrderComparator.sort(sortedSyncs);
        return Collections.unmodifiableList(sortedSyncs);
    }


    public static void clearSynchronization(){
        if(!isSynchronizationActive()){
            throw new IllegalStateException("Transaction synchronization未激活，不能设置为无效");
        }
        synchronizations.remove();
    }


    public static Object getResource(Object key){
        Map<Object, Object> map = resources.get();
        if (map != null) {
            return map.get(key);
        }
        return null;
    }

    public static void bindResource(Object key, Object value){
        Map<Object, Object> map = resources.get();
        if (map == null) {
            map = new HashMap<>();
            resources.set(map);
        }

        Object oldValue = map.put(key, value);

        if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
            oldValue = null;
        }
    }

    public static Object unbindResource(Object key){
        Map<Object, Object> map = resources.get();
        if (map == null) {
            return null;
        }
        Object value = map.remove(key);
        if (map.isEmpty()) {
            resources.remove();
        }

        if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
            value = null;
        }
        return value;
    }
}
